package com.ysstech.etfmanage.service.impl;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.ysstech.common.enums.YssEnum;
import com.ysstech.common.exception.BusinessException;
import com.ysstech.common.service.HolidayService;
import com.ysstech.common.util.BigDecimalUtil;
import com.ysstech.common.util.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ysstech.etfmanage.entity.Account;
import com.ysstech.etfmanage.entity.AccountFill;
import com.ysstech.etfmanage.entity.ParamVo;
import com.ysstech.etfmanage.entity.Rights;
import com.ysstech.etfmanage.enums.EtfManageEnum;
import com.ysstech.etfmanage.feign.entity.Quotation;
import com.ysstech.etfmanage.feign.entity.SecuritiesDealDetailed;
import com.ysstech.etfmanage.mapper.AccountFillMapper;
import com.ysstech.etfmanage.service.AccountFillService;
import com.ysstech.etfmanage.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lishuangliang
 * @since 2021-02-10
 */
@Slf4j
@Service
public class AccountFillServiceImpl extends ServiceImpl<AccountFillMapper, AccountFill> implements AccountFillService {

    @Autowired
    private HolidayService holidayService;
    @Autowired
    private AccountFillService accountFillService;
    @Autowired
    private AccountService accountService;

    /**
     * 更新股票台账补票数据
     *
     * @param list    主数据 当天待补的数据按照证券代码排序，申赎日期前的在前面 先汇总后明细
     * @param paramVo etf参数实体  自己成分券对应的参数
     * @return
     * @throws Exception
     */
    @Override
    public void etfAccountFill(List<Account> list, ParamVo paramVo, Map<String, List<SecuritiesDealDetailed>> mapFillDetail) throws Exception {
        List<AccountFill> accountFills = new ArrayList<>();
        List<Account> accountList = new ArrayList<>();
        //所有数据按照证券代码，申购日期，申购赎回标志
        Map<String, List<Account>> mapFill = this.etfhandleDataEtfaccount(list);
        if (CollectionUtils.isEmpty(mapFill)) {
            log.info("没有获取需要补票的数据");
            return;
        }
        for (Map.Entry<String, List<Account>> info : mapFill.entrySet()) {
            List<Account> accounts = info.getValue();
            if (null == accounts || accounts.size() == 0) {
                continue;
            }
            //轧差补票
            if (this.renewNetting(paramVo)) {
                accountFills.addAll(this.renewNettingFill(accounts, mapFill, paramVo));
            }
            //普通补票
            accountFills.addAll(this.renewPlainFill(accounts, paramVo, mapFillDetail));
            //强制补票
            accountFills.addAll(this.renewForceFill(accounts, paramVo));
            accountList.addAll(accounts);
        }
        //批量更新补票数据
        if (!CollectionUtils.isEmpty(accountFills)) {
            accountFillService.saveBatch(accountFills);
        }
        //更新台账剩余数据
        if (!CollectionUtils.isEmpty(accountList)) {
            accountService.updateBatchById(accountList, Integer.valueOf(YssEnum.YSS_BATCH_SIZE.getCode()));
        }
    }

    //判断是否需要轧差补票
    private Boolean renewNetting(ParamVo paramVo) {
        if (3 == paramVo.getEtfRenewType() || 4 == paramVo.getEtfRenewType()) {
            return true;
        }
        return false;
    }

    //判断是否先到先补的模式
    private Boolean firstComeFirst(ParamVo paramVo) {
        if (2 == paramVo.getEtfRenewType() || 4 == paramVo.getEtfRenewType()) {
            return true;
        }
        return false;
    }

    /**
     * 将待补的一个券拆分成 按照申购日期的集合，分别去补
     *
     * @param list
     * @return
     */
    private Map<String, List<Account>> etfhandleDataEtfaccount(List<Account> list) throws Exception {
        Map<String, List<Account>> map = new LinkedHashMap<>();
        if (CollectionUtils.isEmpty(list)) {
            return map;
        }
        for (Account etfaccount : list) {
            if (null == etfaccount) {
                continue;
            }
            String key = etfaccount.getSecurityCode() + "\t" + etfaccount.getFdate() + "\t" + etfaccount.getBusinessType() + "\t" + etfaccount.getMarket();
            List<Account> accounts = null;
            if (map.containsKey(key)) {
                accounts = map.get(key);
                if (null == accounts || accounts.size() == 0) {
                    continue;
                }
                accounts.add(etfaccount);
                map.put(key, accounts);
            } else {
                accounts = new ArrayList<>();
                accounts.add(etfaccount);
                map.put(key, accounts);
            }
        }
        return map;
    }

    /**
     * 轧差补票
     *
     * @param accounts
     * @param mapFill
     * @return
     * @throws Exception
     */
    private List<AccountFill> renewNettingFill(List<Account> accounts, Map<String, List<Account>> mapFill, ParamVo paramVo) throws Exception {
        Account sumEtfaccount = null;
        AccountFill accountFillVoSum = null;
        List<AccountFill> list = new ArrayList<>();
        for (int i = 0; i < accounts.size(); i++) {
            Account etfaccount = accounts.get(i);
            if (null == etfaccount || EtfManageEnum.ETF_MUST.getCode().equals(etfaccount.getBusinessType())
                    || etfaccount.getListAccountFill().size() > 0) {
                continue;
            }
            Quotation quotation = etfaccount.getQuotation();
            if (null == quotation || !quotation.getFdate().isEqual(DateUtil.strToLocalDate(etfaccount.getPurchaseDate()))) {
                continue;
            }
            BigDecimal organizationPrice = (1 == paramVo.getEtfOrganizationPrice()) ? quotation.getQuotationClosingPrice()
                    : quotation.getQuotationAveragePrice();
            if (EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode().equals(etfaccount.getShareholderCode())) {
                //获取对应的待买待卖总数
                sumEtfaccount = this.getEtfSumAmount(etfaccount, mapFill);
                //处理汇总轧差补票
                accountFillVoSum = this.renewNettingFillSum(sumEtfaccount, organizationPrice);
                if (null == accountFillVoSum) {
                    continue;
                }
                etfaccount.getListAccountFill().add(accountFillVoSum);
                list.add(accountFillVoSum);
            } else {
                boolean islast = (i == accounts.size() - 1) ? true : false;  //是否最后一条明细
                AccountFill detailFill = this.renewNettingFillDetail(etfaccount, sumEtfaccount, islast, accountFillVoSum, organizationPrice);
                if (null == detailFill) {
                    continue;
                }
                etfaccount.getListAccountFill().add(detailFill);
                list.add(detailFill);
            }
        }
        return list;
    }

    /**
     * 集中申购 获取待买待卖的总数量,获取申购日期同一天的进行轧差补票
     *
     * @param account
     * @param listdetail
     * @return
     * @throws Exception
     */
    private Account getEtfSumAmount(Account account, Map<String, List<Account>> listdetail) throws Exception {
        BigDecimal sumSellAmount = BigDecimal.ZERO, sumBuyAmount = BigDecimal.ZERO; //申购和赎回的总数量
        String keysell = account.getSecurityCode() + "\t" + account.getFdate() + "\t"
                + EtfManageEnum.ETF_PURCHASE.getCode() + "\t" + account.getMarket();
        String keysbuy = account.getSecurityCode() + "\t" + account.getFdate() + "\t"
                + EtfManageEnum.ETF_REDEMPTION.getCode() + "\t" + account.getMarket();
        if (listdetail.containsKey(keysell)) { //获取待卖的总数
            for (Account bean : listdetail.get(keysell)) {
                if (null != bean && EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode().equals(bean.getShareholderCode())) {
                    sumSellAmount = BigDecimalUtil.add(sumSellAmount, bean.getAmount());
                }
            }
        }
        if (listdetail.containsKey(keysbuy)) { //获取待买的总数
            for (Account bean : listdetail.get(keysbuy)) {
                if (null != bean && EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode().equals(bean.getShareholderCode())) {
                    sumBuyAmount = BigDecimalUtil.add(sumBuyAmount, bean.getAmount());
                }
            }
        }
        account.setSumSellAmount(sumSellAmount); //申购
        account.setSumBuyAmount(sumBuyAmount); //赎回
        return account;
    }

    /**
     * 处理轧差补票的汇总记录
     *
     * @param account
     * @param quotationPrice 行情价格
     * @return
     * @throws Exception
     */
    private AccountFill renewNettingFillSum(Account account, BigDecimal quotationPrice) throws Exception {
        AccountFill accountFillVo = new AccountFill();
        //默认轧差数量取待买待卖的数量
        BigDecimal amount = account.getAmount();
        //待买 ,待买的数量大于待卖的数量，待买轧差补票直接取待卖的数量。待卖，待买数量小于待卖数量直接取待卖数量
        if (EtfManageEnum.ETF_PURCHASE.getCode().equals(account.getBusinessType())
                && account.getSumSellAmount().compareTo(account.getSumBuyAmount()) >= 0) {
            amount = account.getSumBuyAmount();
        } else if (EtfManageEnum.ETF_REDEMPTION.getCode().equals(account.getBusinessType())
                && account.getSumSellAmount().compareTo(account.getSumBuyAmount()) < 0) {
            amount = account.getSumSellAmount();
        }
        accountFillVo.setAccountId(account.getId());
        accountFillVo.setId(DateUtil.generateUUID());
        accountFillVo.setFdate(DateUtil.strToLocalDate(account.getPurchaseDate()));
        accountFillVo.setType(Integer.valueOf(EtfManageEnum.ETF_FILL_NETTING_CODE.getCode())); //轧差补票
        if (BigDecimalUtil.compareTo(amount, BigDecimal.ZERO) <= 0) { //只有待买或者待卖，直接返回都是0
            return accountFillVo;
        }
        //获取权益数据
        Account etfRight = this.getEtfRights(account.getListRights());
        BigDecimal replaceAmount = BigDecimal.ZERO, payableReplaceAmount = BigDecimal.ZERO;
        BigDecimal cost = BigDecimalUtil.multiply(amount, quotationPrice, 2);
        if (EtfManageEnum.ETF_PURCHASE.getCode().equals(account.getBusinessType())) { //待买
            replaceAmount = BigDecimalUtil.divide(amount.multiply(account.getReplaceMoney().subtract(etfRight.getSumRightsRealDividendMoney()).subtract(etfRight.getSumRightsRealValueMoney())),
                    account.getAmount().add(etfRight.getSumRightsRealAmount()));
        } else if (EtfManageEnum.ETF_REDEMPTION.getCode().equals(account.getBusinessType())) { //待卖
            replaceAmount = BigDecimalUtil.divide(amount.multiply(account.getReplaceMoney().add(etfRight.getSumRightsRealDividendMoney()).add(etfRight.getSumRightsRealValueMoney())),
                    account.getAmount().add(etfRight.getSumRightsRealAmount()));
        }
        payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, cost);
        //轧差补票数量/（替代数量+总权益数量）*总可退
        BigDecimal sumReplaceableAmount = BigDecimalUtil.divide(amount.multiply(account.getReplaceableMoney()),
                account.getAmount().add(etfRight.getSumRightsRealAmount()));
        BigDecimal sumUnitCost = quotationPrice.setScale(Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()), BigDecimal.ROUND_HALF_UP);
        accountFillVo.setAmount(amount); //补票数量
        accountFillVo.setCost(cost);
        accountFillVo.setReplaceMoney(replaceAmount);
        accountFillVo.setPayableReplaceMoney(payableReplaceAmount);
        accountFillVo.setReplaceableMoney(sumReplaceableAmount);
        accountFillVo.setUnitCost(sumUnitCost);
        return accountFillVo;
    }


    /**
     * 处理轧差补票的明细的记录
     *
     * @param sumEtfaccount
     * @param islast
     * @param accountFillVoSum
     * @return
     * @throws Exception
     */
    private AccountFill renewNettingFillDetail(Account vo, Account sumEtfaccount, boolean islast,
                                               AccountFill accountFillVoSum, BigDecimal quotationPrice) throws Exception {
        AccountFill accountFillVo = new AccountFill();
        accountFillVo.setFdate(DateUtil.strToLocalDate(vo.getPurchaseDate()));
        accountFillVo.setAccountId(vo.getId());
        accountFillVo.setId(DateUtil.generateUUID());
        accountFillVo.setType(Integer.valueOf(EtfManageEnum.ETF_FILL_NETTING_CODE.getCode())); //轧差补票
        if (null == accountFillVoSum || accountFillVo.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            return accountFillVo;
        }
        BigDecimal amount = vo.getAmount();
        if (islast) { //最后一条明细 倒减
            amount = BigDecimalUtil.subtract(accountFillVoSum.getAmount(),
                    accountFillVoSum.getCountFillAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                return accountFillVo;
            }
            accountFillVo.setAmount(amount);
            accountFillVo.setCost(BigDecimalUtil.subtract(accountFillVoSum.getCost(), accountFillVoSum.getCountFillCost()));
            accountFillVo.setReplaceMoney(BigDecimalUtil.subtract(accountFillVoSum.getReplaceMoney(), accountFillVoSum.getCountFillReplaceMoney()));
            accountFillVo.setPayableReplaceMoney(BigDecimalUtil.subtract(accountFillVoSum.getPayableReplaceMoney(), accountFillVoSum.getCountFillPayableReplaceMoney()));
            accountFillVo.setReplaceableMoney(BigDecimalUtil.subtract(accountFillVoSum.getReplaceableMoney(), accountFillVoSum.getCountFillReplaceMoney()));
            accountFillVo.setUnitCost(accountFillVoSum.getUnitCost()); //取汇总记录的单位成本
            return accountFillVo;
        } else {
            if (sumEtfaccount.getSumSellAmount().compareTo(sumEtfaccount.getSumBuyAmount()) >= 0
                    && EtfManageEnum.ETF_PURCHASE.getCode().equals(sumEtfaccount.getBusinessType())
                    && sumEtfaccount.getSumBuyAmount().compareTo(BigDecimal.ZERO) > 0) {
                amount = BigDecimalUtil.divide(BigDecimalUtil.multiply(amount, sumEtfaccount.getSumBuyAmount())
                        , sumEtfaccount.getSumSellAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            } else if (sumEtfaccount.getSumSellAmount().compareTo(sumEtfaccount.getSumBuyAmount()) < 0
                    && EtfManageEnum.ETF_REDEMPTION.getCode().equals(sumEtfaccount.getBusinessType())
                    && sumEtfaccount.getSumBuyAmount().compareTo(BigDecimal.ZERO) > 0) {
                amount = BigDecimalUtil.divide(BigDecimalUtil.multiply(amount, sumEtfaccount.getSumSellAmount())
                        , sumEtfaccount.getSumBuyAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            }
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                return accountFillVo;
            }
            BigDecimal replaceAmount = BigDecimal.ZERO, payableReplaceAmount = BigDecimal.ZERO;
            BigDecimal cost = BigDecimalUtil.multiply(amount, quotationPrice, Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            //获取权益数据
            Account etfRight = this.getEtfRights(vo.getListRights());
            if (EtfManageEnum.ETF_PURCHASE.getCode().equals(vo.getBusinessType())) { //待买
                replaceAmount = BigDecimalUtil.divide(amount.multiply(vo.getReplaceMoney().subtract(etfRight.getSumRightsRealDividendMoney()).subtract(etfRight.getSumRightsRealValueMoney())),
                        vo.getAmount().add(etfRight.getSumRightsRealAmount()));
            } else if (EtfManageEnum.ETF_REDEMPTION.getCode().equals(vo.getBusinessType())) { //待卖
                replaceAmount = BigDecimalUtil.divide(amount.multiply(vo.getReplaceMoney().add(etfRight.getSumRightsRealDividendMoney()).add(etfRight.getSumRightsRealValueMoney())),
                        vo.getAmount().add(etfRight.getSumRightsRealAmount()));
            }
            payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, cost);
            BigDecimal detailPayableReplaceMoney = BigDecimalUtil.divide(amount.multiply(vo.getReplaceableMoney()),
                    vo.getAmount().add(etfRight.getSumRightsRealAmount()));
            accountFillVoSum.setCountFillAmount(BigDecimalUtil.add(accountFillVoSum.getCountFillAmount(), amount, Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode())));
            accountFillVoSum.setCountFillCost(BigDecimalUtil.add(accountFillVoSum.getCountFillCost(), cost));
            accountFillVoSum.setCountFillReplaceMoney(BigDecimalUtil.add(accountFillVoSum.getCountFillReplaceMoney(), replaceAmount));
            accountFillVoSum.setCountFillPayableReplaceMoney(BigDecimalUtil.add(accountFillVoSum.getCountFillPayableReplaceMoney(), payableReplaceAmount));
            accountFillVoSum.setCountFillReplaceableMoney(BigDecimalUtil.add(accountFillVoSum.getCountFillReplaceableMoney(), detailPayableReplaceMoney));
            accountFillVo.setAmount(amount);
            accountFillVo.setCost(cost);
            accountFillVo.setReplaceMoney(replaceAmount);
            accountFillVo.setPayableReplaceMoney(payableReplaceAmount);
            accountFillVo.setReplaceableMoney(detailPayableReplaceMoney);
            accountFillVo.setUnitCost(accountFillVoSum.getUnitCost()); //取汇总记录的单位成本
        }

        return accountFillVo;
    }


    /**
     * 普通补票处理 直接返回补票的数据
     *
     * @param accountList
     * @param paramVo
     * @param mapFillDetail
     * @return
     * @throws Exception
     */
    private List<AccountFill> renewPlainFill(List<Account> accountList, ParamVo paramVo, Map<String, List<SecuritiesDealDetailed>> mapFillDetail) throws Exception {
        List<AccountFill> accountFills = new ArrayList<>(); //返回集合带需要更新的补票集合
        AccountFill sumAccountFill = null;
        Account sumAccount = null; //汇总记录对象，先到先补明细补完才更新汇总的记录
        for (int i = 0; i < accountList.size(); i++) {
            Account account = accountList.get(i);
            if (null == account) {
                continue;
            }
            // 明细交易数据
            List<SecuritiesDealDetailed> beanList = null;
            if (!CollectionUtils.isEmpty(mapFillDetail)) {
                beanList = mapFillDetail.get(account.getSecurityCode() + "\t" + account.getBusinessType());
                account = this.getSumFill(account, beanList);
            }
            if (!this.isPlainFill(account, paramVo)) {
                continue;
            }
            if (EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode().equals(account.getShareholderCode())
                    && BigDecimalUtil.compareTo(account.getFillAmount(), BigDecimal.ZERO) >= 0) {  //计算汇总记录
                sumAccountFill = this.renewPlainFillSum(account, accountList, paramVo);
                if (null == sumAccountFill) {
                    continue;
                }
                if (!this.firstComeFirst(paramVo)) { //单市场直接添加
                    account.getListAccountFill().add(sumAccountFill);
                    accountFills.add(sumAccountFill);
                    // 如果是最后一笔调整尾差到最后一天补券的汇总数据上
                }
                sumAccount = account;
            } else {
                boolean islast = (i == accountList.size() - 1) ? true : false;  //是否最后一条明细
                AccountFill etfaccountFillVoDetail = this.renewPlainFillDetail(account, sumAccountFill, islast, beanList, paramVo, sumAccount);
                if (null == etfaccountFillVoDetail) {
                    continue;
                }
                account.getListAccountFill().add(etfaccountFillVoDetail);
                accountFills.add(etfaccountFillVoDetail);
                //上海最后，更新汇总的为明细之和
                if (null == sumAccountFill || !islast || !this.firstComeFirst(paramVo) || null == sumAccount) {
                    continue;
                }
                sumAccountFill.setAmount(sumAccountFill.getCountFillAmount());
                sumAccountFill.setReplaceMoney(sumAccountFill.getCountFillReplaceMoney());
                sumAccountFill.setReplaceableMoney(sumAccountFill.getCountFillReplaceableMoney());
                sumAccountFill.setPayableReplaceMoney(sumAccountFill.getCountFillPayableReplaceMoney());
                sumAccountFill.setCost(sumAccountFill.getCountFillCost());
                sumAccount.getListAccountFill().add(sumAccountFill);
                accountFills.add(sumAccountFill);
            }
        }
        return accountFills;
    }

    /**
     * 判断是否需要补票
     *
     * @param vo
     * @param paramVo
     * @return
     */
    private boolean isPlainFill(Account vo, ParamVo paramVo) throws Exception {
        if (null == vo || null == paramVo) {
            return false;
        }

        Quotation quotation = vo.getQuotation();//获取当天行情
        if (null != quotation && quotation.getFdate().isEqual(DateUtil.strToLocalDate(vo.getPurchaseDate()))) { //当天有行情的时候补票
            return true;
        }
        //当天没有行情 跨市场T+0的申赎当天停牌也算一次补票
        if (this.firstComeFirst(paramVo) && vo.getFdate().isEqual(DateUtil.strToLocalDate(vo.getPurchaseDate()))) {
            return true;
        }

        return false;
    }

    /**
     * 只针对上海市场的券，深圳的券是用明细计算的汇总上去的
     * 当天补券的最后一笔放入map中，补到这条数据的时候直接用导减的成本计算
     * 最后一日的补券成本汇总=多日补券总成本— 第1日的补券成本汇总— 第2日的补券成本汇总-— ……—第N-1日的补券成本汇总
     * 上海的券单位成本都一样
     *
     * @param listFill
     * @return
     * @throws Exception
     */
    private Map<String, Account> getLastFill(List<Account> listFill) throws Exception {
        Map<String, Account> map = new HashMap<String, Account>();
        if (CollectionUtils.isEmpty(listFill)) {
            return null;
        }
        // 只会保留当天申赎的最后一个补券数据
        for (Account account : listFill) {
            if (null == account) {
                continue;
            }
            map.put(account.getSecurityCode() + "\t" + account.getFdate(), account);
        }
        return map;
    }

    /**
     * 普通汇总记录的补票数据
     *
     * @param account
     * @return
     */
    private AccountFill renewPlainFillSum(Account account, List<Account> list, ParamVo paramVo) throws Exception {
        AccountFill accountFillVo = new AccountFill();

        boolean isample = false; //是否够补
        BigDecimal fillAmount = account.getFillAmount();
        BigDecimal fillMoney = account.getFillMoney();
        Account bean = this.getMakeFill(account.getListAccountFill()); //获取已使用的数量替代金额，可退替代金额
        //汇总记录的数量，替代金额，可退替代金额，应付替代金额，补券成本
        BigDecimal amount = BigDecimal.ZERO, replaceAmount = BigDecimal.ZERO,
                replaceableAmount = BigDecimal.ZERO, payableReplaceAmount = BigDecimal.ZERO,
                cost = BigDecimal.ZERO;
        //获取权益数据
        Account etfRight = this.getEtfRights(account.getListRights());
        if (fillAmount.compareTo(account.getAmount().add(etfRight.getSumRightsRealAmount()).subtract(bean.getMakeFillAmount())) < 0) { //不够补
            amount = fillAmount;
            if (paramVo.getEtfSecondFill()) {  //碎股二次补票，计算明细实际补票数量
                this.secondFill(amount, account.getAmount().add(etfRight.getSumRightsRealAmount()), list, bean);
            }
        } else { //够补
            amount = account.getAmount().add(etfRight.getSumRightsRealAmount()).subtract(bean.getMakeFillAmount());
            isample = true;
        }
        accountFillVo.setFdate(DateUtil.strToLocalDate(account.getPurchaseDate()));
        accountFillVo.setType(Integer.valueOf(EtfManageEnum.ETF_FILL_PLAIN_CODE.getCode())); //普通补票
        accountFillVo.setAmount(amount);
        accountFillVo.setAccountId(account.getId());
        accountFillVo.setId(DateUtil.generateUUID());
        accountFillVo.setAddTime(LocalDateTime.now());
        //当天买入的单位成本  = 汇总接口清算买入金额 /买入的数量  上海和深圳计算一样
        if (BigDecimalUtil.compareTo(amount, BigDecimal.ZERO) <= 0) {
            return accountFillVo;
        }
        BigDecimal sumFillDwcb = BigDecimalUtil.divide(account.getFillMoney(), account.getFillAmount(),
                Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        if (isample) { //够补
            replaceAmount = BigDecimalUtil.subtract(BigDecimalUtil.add(BigDecimalUtil.add(account.getReplaceMoney(), etfRight.getSumRightsRealDividendMoney()),
                    etfRight.getSumRightsRealValueMoney()), bean.getMakeFillReplaceMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            replaceableAmount = BigDecimalUtil.subtract(account.getReplaceableMoney(), bean.getMakeFillReplaceableMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        } else {
            //替代金额 可退替代 深圳上海一样 补票没补完
            replaceAmount = BigDecimalUtil.divide(BigDecimalUtil.multiply(amount, BigDecimalUtil.subtract(BigDecimalUtil.subtract(account.getReplaceMoney(),
                    etfRight.getSumRightsDividendMoney()), etfRight.getSumRightsValueMoney())), BigDecimalUtil.add(account.getAmount(), etfRight.getSumRightsAmount()),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            replaceableAmount = BigDecimalUtil.divide(BigDecimalUtil.multiply(account.getReplaceableMoney(), amount),
                    BigDecimalUtil.add(account.getAmount(), etfRight.getSumRightsRealAmount()),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        }
        if (this.firstComeFirst(paramVo)) {
            cost = BigDecimalUtil.divide(BigDecimalUtil.multiply(account.getFillMoney(), amount), account.getFillAmount(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, cost,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        } else if (fillAmount.compareTo(BigDecimal.ZERO) > 0) {
            //当天有多笔补票的时候，总的清算金额减去已使用的清算金额， 重新计算可能会有尾差 所以记录每次的，下一次都是用总的减去已使用的
            payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, BigDecimalUtil.divide(BigDecimalUtil.multiply(fillMoney, amount), fillAmount,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
            cost = BigDecimalUtil.subtract(replaceAmount, payableReplaceAmount,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            fillMoney = BigDecimalUtil.divide(BigDecimalUtil.multiply(amount, fillMoney), fillAmount,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())); //已使用的清算金额
        }
        accountFillVo.setMakeDealAmount(amount);//已使用的清算数量
        accountFillVo.setMakeDealMoney(fillMoney);//已使用的清算金额
        accountFillVo.setUnitCost(sumFillDwcb);
        accountFillVo.setReplaceMoney(replaceAmount);
        accountFillVo.setReplaceableMoney(replaceableAmount);
        accountFillVo.setCost(cost);
        accountFillVo.setPayableReplaceMoney(payableReplaceAmount);
        return accountFillVo;
    }

    /**
     * 补票处理时碎股继续进行二次补票
     * 不够补   总的补票数量10  总的申购数量20 比例0.5
     * 第一条明细申购为5  5 * 0.5=2.5   3
     * 第二条明细申购为5  5 * 0.3=2.5   3
     * 第三条明细申购为5  5 * 0.3=2.5   3
     * 第四条明细申购为5  5 * 0.3=2.5   3  这种情况最后一条倒减不会影响
     * <p>
     * 不够补   总的补票数量15  总的申购数量20 比例0.75
     * 第一条明细申购为5  5 * 0.75=3.75   4
     * 第二条明细申购为5  5 * 0.75=3.75   4
     * 第三条明细申购为8  8 * 0.75=6     6
     * 第四条明细申购为2  2 * 0.75=1.5   2
     *
     * @param sumFillAmount     补票的总数
     * @param sumPurchaseAmount 申购的总数
     * @param list
     */
    private List<? extends Account> secondFill(BigDecimal sumFillAmount, BigDecimal sumPurchaseAmount, List<? extends Account> list, Account makebean) throws Exception {
        if (CollectionUtils.isEmpty(list) || sumFillAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return list;
        }
        BigDecimal countFillAmount = BigDecimal.ZERO; //明细汇总的补票数量
        for (Account bean : list) {
            if (null != bean && !EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode().equals(bean.getShareholderCode())) { //排除汇总，重明细的开始
                //明细待补票数量   取整数
                BigDecimal detailFillAmount = BigDecimalUtil.divide(BigDecimalUtil.multiply(bean.getAmount(), sumFillAmount), sumPurchaseAmount,
                        Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
                bean.setFillAmount(detailFillAmount); //第一次补票
                countFillAmount = BigDecimalUtil.add(countFillAmount, detailFillAmount, Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            }
        }
        //明细汇总的补票数量和汇总记录的补票数量不相等的时候
        if (BigDecimalUtil.compareTo(countFillAmount, sumFillAmount) == 0) {
            return list;
        }
        for (int i = list.size() - 1; i > 0; i--) { //明细记录由最大股东代码的记录倒着循环
            Account bean = list.get(i);
            if (null == bean || null == makebean) {
                continue;
            }
            // 明细实际待补数量 (可以补票的数量)
            BigDecimal realStayFillAmount = BigDecimalUtil.subtract(bean.getAmount(), makebean.getMakeFillAmount(),
                    Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            // 明细实际补票数量
            BigDecimal realFillAmount = BigDecimalUtil.divide(BigDecimalUtil.multiply(bean.getAmount(), sumFillAmount), sumPurchaseAmount,
                    Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            if (BigDecimalUtil.compareTo(realStayFillAmount, realFillAmount) <= 0) {
                continue;
            }
            //待补数量为5，实际补票数量按照比例四舍五入只有4,表示这里还可以补一票
            //补票的数量和待补的数量的差 大于汇总的记录和明细汇总之差
            if (BigDecimalUtil.subtract(realStayFillAmount, realFillAmount).compareTo(BigDecimalUtil.subtract(sumFillAmount, countFillAmount)) > 0) {
                bean.setFillAmount(BigDecimalUtil.add(realFillAmount, BigDecimalUtil.subtract(sumFillAmount, countFillAmount),
                        Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode())));
                break;
            } else {
                bean.setFillAmount(realFillAmount);// 设置明细的所有补票为待补数量
            }
        }
        return list;
    }

    /**
     * 普通明细记录的补票数据
     *
     * @param account        明细记录
     * @param sumAccountFill 汇总记录
     * @param islast         是否最后一条
     * @param beanList       明细补票数据
     * @return
     */
    private AccountFill renewPlainFillDetail(Account account, AccountFill sumAccountFill, boolean islast,
                                             List<SecuritiesDealDetailed> beanList, ParamVo paramVo, Account sumAccount) throws Exception {
        AccountFill accountFillVo = new AccountFill();
        //先进先出，按顺序补，后面没有就不补
        BigDecimal amount = BigDecimal.ZERO, replaceAmount = BigDecimal.ZERO, replaceableAmount = BigDecimal.ZERO,
                payableReplaceAmount = BigDecimal.ZERO, cost = BigDecimal.ZERO, unitCost = BigDecimal.ZERO;
        accountFillVo.setFdate(DateUtil.strToLocalDate(account.getPurchaseDate()));
        accountFillVo.setType(Integer.valueOf(EtfManageEnum.ETF_FILL_PLAIN_CODE.getCode())); //普通补票
        accountFillVo.setAmount(amount);
        accountFillVo.setAccountId(account.getId());
        accountFillVo.setId(DateUtil.generateUUID());
        accountFillVo.setAddTime(LocalDateTime.now());
        if (null == sumAccountFill || BigDecimalUtil.compareTo(sumAccountFill.getAmount(), BigDecimal.ZERO) <= 0 ||
                BigDecimalUtil.compareTo(sumAccountFill.getAmount(), BigDecimal.ZERO) <= 0) { //有行情没有不上，也记录一次补票
            return accountFillVo;
        }
        //获取已使用的数量替代金额，可退替代金额
        Account bean = this.getMakeFill(account.getListAccountFill());
        //获取权益数据
        Account etfRight = this.getEtfRights(account.getListRights());
        //先到先补模式的或者够补直接不，直接计算补票数量
        if (this.firstComeFirst(paramVo) || sumAccountFill.getAmple()) {
            amount = BigDecimalUtil.subtract(BigDecimalUtil.add(account.getAmount(), etfRight.getSumRightsRealAmount()), bean.getMakeFillAmount(),
                    Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
        } else if (paramVo.getEtfSecondFill()) { //如果勾选了“补票处理时碎股继续进行二次补票”，则对未补完的股票进行二次补票
            amount = BigDecimalUtil.round(account.getFillAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
        } else {
            amount = BigDecimalUtil.divide(BigDecimalUtil.multiply(sumAccountFill.getAmount(), BigDecimalUtil.add(account.getAmount(), etfRight.getSumRightsRealAmount())),
                    sumAccount.getAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
        }
        if (BigDecimalUtil.compareTo(amount, BigDecimal.ZERO) <= 0) { //有行情没有不上，也记录一次补票
            return accountFillVo;
        }
        if (islast && !this.firstComeFirst(paramVo)) { // 不是先到先补的模式
            amount = BigDecimalUtil.subtract(sumAccountFill.getAmount(), sumAccountFill.getCountFillAmount(),
                    Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            replaceAmount = BigDecimalUtil.subtract(sumAccountFill.getReplaceMoney(), sumAccountFill.getCountFillReplaceMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            replaceableAmount = BigDecimalUtil.subtract(sumAccountFill.getReplaceableMoney(), sumAccountFill.getCountFillReplaceableMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            payableReplaceAmount = BigDecimalUtil.subtract(sumAccountFill.getPayableReplaceMoney(), sumAccountFill.getCountFillPayableReplaceMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            cost = BigDecimalUtil.subtract(sumAccountFill.getCost(), sumAccountFill.getCountFillCost(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        } else {
            BigDecimal countsl = sumAccountFill.getCountFillAmount();
            countsl = BigDecimalUtil.add(countsl, amount, Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            if (this.firstComeFirst(paramVo) && BigDecimalUtil.compareTo(sumAccountFill.getAmount(), countsl) < 0) { //上海计算上这次补票的数据已经不够补的情况
                //重新计算明细记录，直接补剩下的数据
                amount = BigDecimalUtil.subtract(sumAccountFill.getAmount(), sumAccountFill.getCountFillAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
                countsl = BigDecimalUtil.add(sumAccountFill.getCountFillAmount(), amount, Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
                if (BigDecimalUtil.compareTo(amount, BigDecimal.ZERO) <= 0) { //有行情没有不上，也记录一次补票
                    return accountFillVo;
                }
            }
            if (BigDecimalUtil.compareTo(BigDecimalUtil.subtract(BigDecimalUtil.add(account.getAmount(), etfRight.getSumRightsRealAmount()), bean.getMakeFillAmount()),
                    amount) <= 0) { //够补
                replaceAmount = BigDecimalUtil.subtract(BigDecimalUtil.add(BigDecimalUtil.add(account.getReplaceMoney(), etfRight.getSumRightsRealDividendMoney()), etfRight.getSumRightsRealValueMoney()),
                        bean.getMakeFillReplaceMoney(), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                replaceableAmount = BigDecimalUtil.subtract(account.getReplaceableMoney(), bean.getMakeFillReplaceableMoney(),
                        Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            } else {
                //替代金额 可退替代 深圳上海一样 补票没补完
                replaceAmount = BigDecimalUtil.divide(BigDecimalUtil.multiply(amount, BigDecimalUtil.subtract(BigDecimalUtil.subtract(account.getReplaceMoney(), etfRight.getSumRightsRealDividendMoney()),
                        etfRight.getSumRightsRealValueMoney())), BigDecimalUtil.add(account.getAmount(), etfRight.getSumRightsRealAmount()),
                        Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                replaceableAmount = BigDecimalUtil.divide(BigDecimalUtil.multiply(account.getReplaceableMoney(), amount), BigDecimalUtil.add(account.getAmount(),
                        etfRight.getSumRightsRealAmount()), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            }
            unitCost = BigDecimalUtil.round(sumAccountFill.getUnitCost(), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            if (this.firstComeFirst(paramVo)) { //先到先补
                //处理上海的补票数据，按照明细记录来补票
                Account detailbean = this.etfAccountDefiniteFill(amount, beanList);
                if (null == detailbean || null == detailbean.getFillAmount() || detailbean.getFillAmount().compareTo(BigDecimal.ZERO) == 0) {
                    return accountFillVo;
                }
                unitCost = BigDecimalUtil.divide(detailbean.getFillMoney(), detailbean.getFillAmount());
                cost = BigDecimalUtil.multiply(BigDecimalUtil.divide(detailbean.getFillMoney(), detailbean.getFillAmount(),
                        Integer.valueOf(EtfManageEnum.ETF_MOVING_WEIGHTED_SCALE.getCode())),
                        amount, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, cost, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            } else { //加权平均的模式
                //应付替代款=替代金额-当日买入该股票总成本/当日买入该股票总数量*本次补票数量
                cost = BigDecimalUtil.multiply(BigDecimalUtil.divide(account.getFillMoney(), account.getFillAmount(),
                        Integer.valueOf(EtfManageEnum.ETF_MOVING_WEIGHTED_SCALE.getCode())),
                        amount, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, cost, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            }
            sumAccountFill.setCountFillAmount(countsl);
            sumAccountFill.setCountFillReplaceMoney(BigDecimalUtil.add(sumAccountFill.getCountFillReplaceMoney(), replaceAmount,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
            sumAccountFill.setCountFillReplaceableMoney(BigDecimalUtil.add(sumAccountFill.getCountFillReplaceableMoney(), replaceableAmount,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
            sumAccountFill.setCountFillPayableReplaceMoney(BigDecimalUtil.add(sumAccountFill.getCountFillPayableReplaceMoney(), payableReplaceAmount,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
            sumAccountFill.setCountFillCost(BigDecimalUtil.add(sumAccountFill.getCountFillCost(), cost,
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
        }
        accountFillVo.setAmount(amount);
        accountFillVo.setUnitCost(unitCost);
        accountFillVo.setCost(cost);
        accountFillVo.setReplaceMoney(replaceAmount);
        accountFillVo.setReplaceableMoney(replaceableAmount);
        accountFillVo.setPayableReplaceMoney(payableReplaceAmount);
        return accountFillVo;
    }

    /**
     * 补票每次直接取更新缓存的数据，将剩余的清算金额和数量也放到缓存中对应的数据上去
     *
     * @param detailAmount 补票数量
     * @param beanList     明细补票数据
     * @return
     */
    private Account etfAccountDefiniteFill(BigDecimal detailAmount, List<SecuritiesDealDetailed> beanList) throws Exception {
        Account account = new Account();
        if (CollectionUtils.isEmpty(beanList)) {
            return null;
        }
        BigDecimal countFillMoney = BigDecimal.ZERO, countFillAmount = BigDecimal.ZERO;
        for (int k = 0; k < beanList.size(); k++) {
            SecuritiesDealDetailed bean = beanList.get(k);
            if (null == bean || BigDecimalUtil.compareTo(bean.getDealAmount(), BigDecimal.ZERO) == 0) {
                continue;
            }
            BigDecimal dealAmount = bean.getDealAmount();
            BigDecimal dealMoney = bean.getDealMoney();
            if (detailAmount.compareTo(dealAmount.add(countFillAmount)) <= 0) { //取到够补的情况
                //剩余的数量
                BigDecimal surplusAmount = BigDecimalUtil.subtract(BigDecimalUtil.add(dealAmount, countFillAmount), detailAmount);
                //使用的清算金额
                BigDecimal makeDealMoney = BigDecimal.ZERO;
                if (dealAmount.compareTo(BigDecimal.ZERO) > 0) {
                    makeDealMoney = BigDecimalUtil.divide(BigDecimalUtil.multiply(BigDecimalUtil.subtract(dealAmount, surplusAmount), dealMoney), dealAmount,
                            Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                }
                //设置当前记录的清算金额和数量  修改缓存中明细数据的清算金额和数量
                account.setFillAmount(detailAmount);
                account.setFillMoney(BigDecimalUtil.add(countFillMoney, makeDealMoney, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
                bean.setDealMoney(BigDecimalUtil.subtract(dealMoney, makeDealMoney, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode())));
                bean.setDealAmount(surplusAmount);
                break;
            } else {
                countFillAmount = BigDecimalUtil.add(countFillAmount, dealAmount);
                countFillMoney = BigDecimalUtil.add(countFillMoney, dealMoney);
                //修改缓存中明细数据的清算金额和数量
                bean.setDealMoney(BigDecimal.ZERO);
                bean.setDealAmount(BigDecimal.ZERO);
                /**
                 * 如果还没补够，直接计算当前的清算金额和数量
                 * 正常不会出现这种情况 台账汇总的记录限制了数量
                 */
                if (k == beanList.size() - 1) { //明细记录最后一条
                    log.error("处理明细补票的数量和清算金额失败！");
                    throw new BusinessException("处理明细补票的数量和清算金额失败");
                }
            }
        }
        return account;
    }


    /**
     * 强制补票处理，最后都会更新剩余数量
     * 满足强制补票条件，剩余数量为0 的更新退款日期和清算标志，不满足根据行情更新补票日期和补票次数
     *
     * @param accountList
     * @param paramVo
     * @return
     * @throws Exception
     */
    private List<AccountFill> renewForceFill(List<Account> accountList, ParamVo paramVo) throws Exception {
        List<AccountFill> accountFills = new ArrayList<>();
        // 强制补票的汇总记录的数据计算 结转日期设置的天数2
        AccountFill accountFill = null;
        for (int i = 0; i < accountList.size(); i++) {
            Account account = accountList.get(i);
            if (null == account) {
                continue;
            }
            String refundDate = holidayService.getNextWorkday(account.getPurchaseDate(), paramVo.getPayableReplaceMoneyDays());
            //普通补票和集中申购满足强制补票
            if (this.forceFill(account, paramVo) && EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode().equals(account.getShareholderCode())) {
                accountFill = this.renewForceFillSum(account, paramVo);
                if (null == accountFill) {
                    continue;
                }
                account.getListAccountFill().add(accountFill); //添加用来计算最后的剩余数量
                account.setClearCode(EtfManageEnum.ETF_CLEAR_CODE_YES.getCode());
                account.setRefundDate(DateUtil.strToLocalDate(refundDate));
                accountFills.add(accountFill);
            } else if (this.forceFill(account, paramVo) && null != accountFill) {
                boolean islast = (i == accountList.size() - 1) ? true : false;  //是否最后一条明细
                AccountFill etfaccountQzBpDetail = this.renewForceFillDetail(account, accountFill, islast);
                if (null == etfaccountQzBpDetail) {
                    continue;
                }
                account.getListAccountFill().add(etfaccountQzBpDetail); //添加用来计算最后的剩余数量
                account.setClearCode(EtfManageEnum.ETF_CLEAR_CODE_YES.getCode());
                account.setRefundDate(DateUtil.strToLocalDate(refundDate));
                accountFills.add(etfaccountQzBpDetail);
            }
            this.renewResidue(account, paramVo); //更新剩余数据
        }
        return accountFills;
    }

    /**
     * 更新台账剩余数据
     *
     * @param etfaccount
     * @return
     */
    private void renewResidue(Account etfaccount, ParamVo paramVo) throws Exception {
        Account bean = this.getMakeFill(etfaccount.getListAccountFill()); //获取已使用的数量替代金额，可退替代金额
        //获取权益数据
        Account etfRight = this.getEtfRights(etfaccount.getListRights());
        //剩余数量
        BigDecimal surplusAmount = BigDecimalUtil.subtract(BigDecimalUtil.add(etfaccount.getAmount(), etfRight.getSumRightsRealAmount()), bean.getMakeFillAmount(), 0);
        // 应退合计金额 等于应付替代款之和
        BigDecimal payableReplaceAmount = BigDecimalUtil.round(bean.getMakeFillPayableReplaceMoney(), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        BigDecimal surplusReplaceAmount = BigDecimalUtil.subtract(etfaccount.getReplaceMoney(), BigDecimalUtil.add(BigDecimalUtil.add(etfRight.getSumRightsRealDividendMoney(),
                etfRight.getSumRightsRealValueMoney()), bean.getMakeFillReplaceMoney()), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        Quotation quotation = etfaccount.getQuotation();
        BigDecimal organizationPrice = BigDecimal.ZERO;
        if (null != quotation) {
            organizationPrice = (1 == paramVo.getEtfOrganizationPrice()) ? quotation.getQuotationClosingPrice() : quotation.getQuotationAveragePrice();
        }
        BigDecimal replaceableValuationAmount = BigDecimal.ZERO;
        if (BigDecimalUtil.compareTo(etfaccount.getAmount(), BigDecimal.ZERO) > 0) { //集中申购的必须可退估值增值为0
            //可退替代总的估值增值= round(剩余未补数量 * (今日收盘价- (申购数量 * 申购单位成本 -权益派息 - 权益价值) / (申购数量 + 权益数量))，2)
            BigDecimal replaceableValuationAmountTemp = BigDecimalUtil.divide(BigDecimalUtil.subtract(BigDecimalUtil.multiply(etfaccount.getAmount(), etfaccount.getUnitCost()),
                    BigDecimalUtil.add(etfRight.getSumRightsRealDividendMoney(), etfRight.getSumRightsRealValueMoney())), BigDecimalUtil.add(etfaccount.getAmount(), etfRight.getSumRightsRealAmount()), 10);
            replaceableValuationAmount = BigDecimalUtil.multiply(surplusAmount, BigDecimalUtil.subtract(organizationPrice, replaceableValuationAmountTemp), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        }
        etfaccount.setSurplusAmount(surplusAmount);
        etfaccount.setTotalPayableReplaceMoney(payableReplaceAmount);
        etfaccount.setSurplusReplaceMoney(surplusReplaceAmount);
        etfaccount.setReplaceableValuationMoney(replaceableValuationAmount);
    }

    /**
     * 判断是否满足强制补票普通场内场外的模式
     *
     * @param etfaccount
     * @return
     */

    private boolean forceFill(Account etfaccount, ParamVo paramVo) throws Exception {
        //强制处理证券交易天数
        int forceTransactionDays = paramVo.getForceTransactionDays();
        //强制处理交易所交易天数
        int forceExchangeDays = paramVo.getForceExchangeDays();
        /*
         * 有行情就会增加一次补票次数，所以直接用补票的次数来判断是否强制
         * 不是先到先补的 强制处理证券交易天数就满足强制补票 也就是T+1
         * 先到先补的，要多一次 T+0
         */
        int countFill = etfaccount.getListAccountFill().size();
        if (this.renewNetting(paramVo)) { //轧差补票
            countFill -= 1;
        }
        if (forceTransactionDays <= countFill) {
            return true;
        }
        String date = holidayService.getNextWorkday(DateUtil.localDateToStr(etfaccount.getFdate()), forceExchangeDays - 1);
        if (DateUtil.differentDaysByString(date, etfaccount.getPurchaseDate()) >= 0) {
            return true;
        }
        return false;
    }

    /**
     * 汇总记录的强制补票
     *
     * @param etfaccount 待补数据
     */
    private AccountFill renewForceFillSum(Account etfaccount, ParamVo paramVo) throws Exception {
        AccountFill accountFill = new AccountFill();
        //汇总记录的数量，替代金额，可退替代金额，应付替代金额，补券成本
        BigDecimal amount = BigDecimal.ZERO, replaceAmount = BigDecimal.ZERO, replaceableAmount = BigDecimal.ZERO,
                payableReplaceAmount = BigDecimal.ZERO, unitCost = BigDecimal.ZERO, cost = BigDecimal.ZERO;
        Account bean = this.getMakeFill(etfaccount.getListAccountFill()); //获取已使用的数量替代金额，可退替代金额
        //获取权益数据
        Account etfRight = this.getEtfRights(etfaccount.getListRights());
        amount = BigDecimalUtil.subtract(BigDecimalUtil.add(etfaccount.getAmount(), etfRight.getSumRightsRealAmount())
                , bean.getMakeFillAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
        accountFill.setFdate(DateUtil.strToLocalDate(etfaccount.getPurchaseDate()));
        accountFill.setType(Integer.valueOf(EtfManageEnum.ETF_FILL_FORCE_CODE.getCode())); //强制补票
        accountFill.setAmount(amount);
        accountFill.setAccountId(etfaccount.getId());
        accountFill.setId(DateUtil.generateUUID());
        accountFill.setAddTime(LocalDateTime.now());
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return accountFill;
        }
        //获取当天行情，当天没有会取最近一天的
        Quotation quotation = etfaccount.getQuotation();
        if (null == quotation) {
            return accountFill;
        }
        unitCost = (1 == paramVo.getEtfOrganizationPrice()) ? quotation.getQuotationClosingPrice() : quotation.getQuotationAveragePrice();
        //替代金额＝roundit(申购日替代金额-实际派息-实际权证价值-第一次补票替代金额-第二次补票替代金额,EtfConstants.SCALE_2)
        replaceAmount = BigDecimalUtil.subtract(etfaccount.getReplaceMoney(), BigDecimalUtil.add(BigDecimalUtil.add(etfRight.getSumRightsRealDividendMoney(),
                etfRight.getSumRightsRealValueMoney()), bean.getMakeFillReplaceMoney()), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        replaceableAmount = BigDecimalUtil.subtract(etfaccount.getReplaceableMoney(), bean.getMakeFillReplaceableMoney(),
                Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        // 应付替代款＝roundit(替代金额 - 该股票最近收盘价*本次强制补票数量,2)
        payableReplaceAmount = BigDecimalUtil.subtract(replaceAmount, BigDecimalUtil.multiply(unitCost, amount), Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        cost = BigDecimalUtil.subtract(replaceAmount, payableReplaceAmount, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        accountFill.setUnitCost(unitCost);
        accountFill.setCost(cost);
        accountFill.setReplaceMoney(replaceAmount);
        accountFill.setReplaceableMoney(replaceableAmount);
        accountFill.setPayableReplaceMoney(payableReplaceAmount);
        return accountFill;
    }

    /**
     * 强制补票明细记录
     *
     * @param etfaccount     待补数据
     * @param etfAccountFill 汇总记录的补票对象
     * @param islast         是否最后条明细
     */
    private AccountFill renewForceFillDetail(Account etfaccount, AccountFill etfAccountFill, boolean islast) throws Exception {
        AccountFill accountFill = new AccountFill();

        // 分记录的  强制处理数据，替代金额，可退替代金额  应付替代款
        BigDecimal amount = BigDecimal.ZERO, replaceAmount = BigDecimal.ZERO,
                replaceableAmount = BigDecimal.ZERO, payableReplaceAmount = BigDecimal.ZERO, cost = BigDecimal.ZERO;
        accountFill.setFdate(DateUtil.strToLocalDate(etfaccount.getPurchaseDate()));
        accountFill.setType(Integer.valueOf(EtfManageEnum.ETF_FILL_FORCE_CODE.getCode())); //强制补票
        accountFill.setAccountId(etfaccount.getId());
        accountFill.setId(DateUtil.generateUUID());
        accountFill.setAddTime(LocalDateTime.now());
        if (null == etfAccountFill) {
            return accountFill;
        }
        // 最后一条记录 用汇总的记录的数量，替代的金额，可退替代金额 减去明细的数量，替代的金额，可退替代金额之和
        if (islast) { //最后一条记录值减去 用总的记录-分记录汇总的差值
            amount = BigDecimalUtil.subtract(etfAccountFill.getAmount(), etfAccountFill.getCountFillAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            replaceAmount = BigDecimalUtil.subtract(etfAccountFill.getReplaceMoney(), etfAccountFill.getCountFillReplaceMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            replaceableAmount = BigDecimalUtil.subtract(etfAccountFill.getReplaceableMoney(), etfAccountFill.getCountFillReplaceableMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            payableReplaceAmount = BigDecimalUtil.subtract(etfAccountFill.getPayableReplaceMoney(), etfAccountFill.getCountFillPayableReplaceMoney(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            cost = BigDecimalUtil.subtract(etfAccountFill.getCost(), etfAccountFill.getCountFillCost(),
                    Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
        } else {
            Account bean = this.getMakeFill(etfaccount.getListAccountFill()); //获取已使用的数量替代金额，可退替代金额
            //获取权益数据
            Account etfRight = this.getEtfRights(etfaccount.getListRights());
            // 强制补票的明细记录的数据计算
            amount = BigDecimalUtil.subtract(BigDecimalUtil.add(etfaccount.getAmount(), etfRight.getSumRightsRealAmount()),
                    bean.getMakeFillAmount(), Integer.valueOf(EtfManageEnum.ETF_AMOUNT_SCALE.getCode()));
            if (amount.compareTo(BigDecimal.ZERO) >= 0) {
                //替代金额＝roundit(申购日替代金额-实际派息-实际权证价值-第一次补票替代金额-第二次补票替代金额,2)
                replaceAmount = BigDecimalUtil.subtract(etfaccount.getReplaceMoney(), BigDecimalUtil.add(BigDecimalUtil.add(
                        etfRight.getSumRightsRealDividendMoney(), etfRight.getSumRightsRealValueMoney()), bean.getMakeFillReplaceMoney()),
                        Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                replaceableAmount = BigDecimalUtil.subtract(etfaccount.getReplaceableMoney(), bean.getMakeFillReplaceableMoney(),
                        Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                // 应付替代款＝roundit(替代金额 - 该股票最近收盘价*本次强制补票数量,2)
                payableReplaceAmount = BigDecimalUtil.subtract(replaceableAmount, BigDecimalUtil.multiply(etfAccountFill.getUnitCost(), amount)
                        , Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
                cost = BigDecimalUtil.subtract(replaceableAmount, payableReplaceAmount, Integer.valueOf(EtfManageEnum.ETF_MONEY_SCALE.getCode()));
            }
            etfAccountFill.setCountFillAmount(BigDecimalUtil.add(etfAccountFill.getCountFillAmount(), amount));
            etfAccountFill.setCountFillReplaceMoney(BigDecimalUtil.add(etfAccountFill.getCountFillReplaceMoney(), replaceAmount));
            etfAccountFill.setCountFillReplaceableMoney(BigDecimalUtil.add(etfAccountFill.getCountFillReplaceableMoney(), replaceableAmount));
            etfAccountFill.setCountFillPayableReplaceMoney(BigDecimalUtil.add(etfAccountFill.getCountFillPayableReplaceMoney(), payableReplaceAmount));
            etfAccountFill.setCountFillCost(BigDecimalUtil.add(etfAccountFill.getCountFillCost(), cost));
        }
        accountFill.setUnitCost(etfAccountFill.getUnitCost());
        accountFill.setAmount(amount);
        accountFill.setReplaceMoney(replaceAmount);
        accountFill.setReplaceableMoney(replaceableAmount);
        accountFill.setPayableReplaceMoney(payableReplaceAmount);
        accountFill.setCost(cost);
        return accountFill;
    }

    /**
     * 汇总当天交易数据的总数量和总金额
     *
     * @param vo
     * @param beanList
     * @return
     * @throws Exception
     */
    private Account getSumFill(Account vo, List<SecuritiesDealDetailed> beanList) throws Exception {
        //已补的总数量,总的可退替代金额,总的可退替代金额
        BigDecimal sumFillAmount = BigDecimal.ZERO, sumFillMoney = BigDecimal.ZERO;
        if (CollectionUtils.isEmpty(beanList)) {
            vo.setFillAmount(sumFillAmount);
            vo.setFillMoney(sumFillMoney);
            return vo;
        }
        for (SecuritiesDealDetailed securitiesDealDetailed : beanList) { //计算已经补票的数量，可退替代金额和替代金额
            sumFillAmount = BigDecimalUtil.add(sumFillAmount, securitiesDealDetailed.getDealAmount());
            sumFillMoney = BigDecimalUtil.add(sumFillMoney, securitiesDealDetailed.getDealMoney());
        }
        vo.setFillAmount(sumFillAmount);
        vo.setFillMoney(sumFillMoney);
        return vo;
    }


    /**
     * 获取补票数据里的已补的总数，替代金额，可退替代金额
     *
     * @param list
     * @return
     */
    private Account getMakeFill(List<AccountFill> list) throws Exception {
        Account bean = new Account();
        //已补的总数量,总的可退替代金额,总的可退替代金额
        BigDecimal makeFillAmount = BigDecimal.ZERO, makeFillReplaceableAmount = BigDecimal.ZERO, makeFillReplaceAmount = BigDecimal.ZERO, makeFillPayableReplaceAmount = BigDecimal.ZERO;
        for (AccountFill bp : list) { //计算已经补票的数量，可退替代金额和替代金额
            makeFillAmount = BigDecimalUtil.add(makeFillAmount, bp.getAmount());
            makeFillReplaceAmount = BigDecimalUtil.add(makeFillReplaceAmount, bp.getReplaceMoney());
            makeFillReplaceableAmount = BigDecimalUtil.add(makeFillReplaceableAmount, bp.getReplaceableMoney());
            makeFillPayableReplaceAmount = BigDecimalUtil.add(makeFillPayableReplaceAmount, bp.getPayableReplaceMoney());
        }
        bean.setMakeFillAmount(makeFillAmount);
        bean.setMakeFillReplaceableMoney(makeFillReplaceableAmount);
        bean.setMakeFillReplaceMoney(makeFillReplaceAmount);
        bean.setMakeFillPayableReplaceMoney(makeFillPayableReplaceAmount);
        return bean;
    }

    /**
     * 获取补票数据里的权益数据
     *
     * @param list
     * @return
     */
    private Account getEtfRights(List<Rights> list) throws Exception {
        Account bean = new Account();

        //权益实际数据，权益实际派息金额
        BigDecimal sumRightsAmount = BigDecimal.ZERO, sumRightsRealAmount = BigDecimal.ZERO,
                sumRightsDividend = BigDecimal.ZERO, sumRightsRealDividend = BigDecimal.ZERO,
                sumRightsValue = BigDecimal.ZERO, sumRightsRealValue = BigDecimal.ZERO;
        for (Rights rights : list) { //计算已经补票的数量，可退替代金额和替代金额
            sumRightsAmount = BigDecimalUtil.add(sumRightsAmount, rights.getAmount());
            sumRightsRealAmount = BigDecimalUtil.add(sumRightsRealAmount, rights.getRealAmount());
            sumRightsDividend = BigDecimalUtil.add(sumRightsDividend, rights.getDividendMoney());
            sumRightsRealDividend = BigDecimalUtil.add(sumRightsRealDividend, rights.getRealDividendMoney());
            sumRightsValue = BigDecimalUtil.add(sumRightsValue, rights.getValueMoney());
            sumRightsRealValue = BigDecimalUtil.add(sumRightsRealValue, rights.getRealValue());
        }
        bean.setSumRightsAmount(sumRightsAmount);
        bean.setSumRightsRealAmount(sumRightsRealAmount);
        bean.setSumRightsDividendMoney(sumRightsDividend);
        bean.setSumRightsRealDividendMoney(sumRightsRealDividend);
        bean.setSumRightsValueMoney(sumRightsValue);
        bean.setSumRightsRealValueMoney(sumRightsRealValue);
        return bean;
    }
}
